import {Injectable} from '@angular/core';
import {ExpandingMenu} from '../entity/expanding-menu';
import {TpStorageService} from './tp-storage.service';
import {ArrayUtils} from '../utils/array-utils';
import {Menu} from '../entity/menu';
import {Chapter} from '../entity/chapter';
import {StringUtils} from '../utils/string-utils';
import {DocMenu} from '../entity/doc-menu';
import {Doc} from '../entity/doc';
import {Artifact} from '../entity/artifact';
import {Section} from '../entity/section';
import {DocApi} from '../entity/doc-api';
import {User} from '../entity/user';
import {TpHttpService} from './tp-http.service';
import {DocFocusService} from './doc-focus.service';
import {MenuRoutes} from './menu-routes';
import {MarkdownMenuNode} from '../entity/markdown-menu-node';
import {MessageService} from './message.service';

@Injectable({
    providedIn: 'root'
})
export class MenuService {
    private markdownMenuIdMap: Map<string, Menu>;

    /**
     * 当前选中菜单
     */
    private selectedMenu: Menu;

    /**
     * 已展开的菜单
     */
    private expandMenusIdSet: Set<string>;

    /**
     * 当前所在文档
     */
    private currentDoc: Doc;

    /**
     * 当前文档ID
     */
    private currentDocId: string;

    /**
     * 当前左菜单ID
     */
    private currentMenuId: string;

    constructor(private docFocusService: DocFocusService,
                private tpStorageService: TpStorageService,
                private messageService: MessageService,
                private httpService: TpHttpService) {
    }

    /**
     * 获取文档索引菜单
     * @param {Artifact[]} docs
     * @param ownDocs
     * @returns {Menu[]}
     */
    public getDocIndexLeftMenus(docs: Artifact[], ownDocs: string[]) {
        this.expandMenusIdSet = this.getExpandMenusIdSet('index');
        const focusDocIds = this.docFocusService.getFocusDocIds();
        const topMenuIdSet = new Set<string>();
        focusDocIds.forEach(item => topMenuIdSet.add(item));

        const myFocusMenus: DocMenu[] = [];
        const otherMenus: DocMenu[] = [];

        if (!ownDocs) {
            ownDocs = [];
        }

        docs.forEach((doc: Artifact, index) => {
            const menuId = `${doc.groupId}:${doc.artifactId}`;

            let name = '';
            if (doc.appId) {
                name = doc.appId;
            } else {
                if (doc.name) {
                    name = doc.name;
                } else {
                    name = doc.artifactId;
                }
            }
            const menu = <DocMenu> {
                index: index,
                id: menuId,
                top: topMenuIdSet.has(menuId),
                name: name,
                groupId: doc.groupId,
                artifactId: doc.artifactId,
                version: doc.version,
                desc: doc.description,
                updateTime: doc.docVersion,
                url: ['/doc', menuId],
                owner: ArrayUtils.contain(ownDocs, menuId)
            };
            if (topMenuIdSet.has(menuId)) {
                myFocusMenus.push(menu);
            } else {
                otherMenus.push(menu);
            }
        });
        const menus: Menu[] = [];
        if (myFocusMenus.length > 0) {
            myFocusMenus.sort(this.compareTo);
            menus.push(<DocMenu>{
                index: 1,
                id: 'my-focus',
                name: '我关注的项目',
                expanding: true,
                children: myFocusMenus
            });
        }
        if (otherMenus.length > 0) {
            otherMenus.sort(this.compareTo);
            menus.push(<DocMenu>{
                index: 2,
                id: 'others',
                name: '其它项目',
                expanding: myFocusMenus.length <= 0 || this.expandMenusIdSet.has('others'),
                children: otherMenus
            });
        }
        return menus;
    }

    /**
     * 获取文档接口菜单
     * @param doc 需要生成菜单的文档
     * @param menuRoutes
     * @param user
     * @returns {Menu[]}
     */
    public getDocLeftMenus(doc: Doc, menuRoutes: MenuRoutes, user: User): Menu[] {
        this.currentDoc = this.initDoc(doc);
        this.currentDocId = `${doc.artifact.groupId}:${doc.artifact.artifactId}`;
        let menus: Menu[] = [];
        this.markdownMenuIdMap = new Map<string, Menu>();
        this.expandMenusIdSet = this.getExpandMenusIdSet(this.currentDocId);
        const mdRoot = this.currentDoc.mdRoot;
        const mdNodes = mdRoot ? mdRoot.getMdNodes() : null;
        if (this.currentDoc.chapters != null) {
            this.currentDoc.chapters.forEach((chapter: Chapter) => {
                const chapterName = StringUtils.getFirstLineStr(chapter.name);
                const menu: DocMenu = <DocMenu>{
                    id: chapterName,
                    icon: 'class',
                    name: chapterName
                };
                const chapterMdNodes = mdNodes && mdNodes.nodes && mdNodes.nodes.get(chapter.name);
                this.setCateReadmeLink(chapterMdNodes, menu, menuRoutes);
                if (chapter.sections) {
                    const subMenus = this.setSectionMenus(chapter, this.currentDoc.artifact, menuRoutes, chapterMdNodes);
                    if (subMenus) {
                        subMenus.forEach(child => child.parent = menu);
                        menu.children = subMenus;
                        menu.expanding = this.expandMenusIdSet.has(menu.id);
                    }
                }
                menus.push(menu);
            });
        }

        if (menus.length === 1 && menus[0].name === '服务接口' && !menus[0].url) {
            menus = menus[0].children;
        }
        // 如果只有一个且未展开时，直接展开
        if (menus.length === 1 && menus[0].children && !menus[0].url) {
            menus[0].expanding = true;
        }

        // 有markdown文档
        this.buildMdMenus(mdNodes, menus, menuRoutes);
        this.addDocManagerMenus(menus, user);

        if (this.selectedMenu) {
            this.selectedMenu.active = false;
        }
        if (menus) {
            menus[0].artifactId = this.currentDocId;
        }
        return menus;
    }

    private buildMdMenus(mdNodes: MarkdownMenuNode, menus: Menu[], menuRoutes: MenuRoutes) {
        if (mdNodes && mdNodes.items && mdNodes.items.size > 0) {
            const mds: Menu[] = [];
            const mdPath = mdNodes.paths;
            const mdMenu = <Menu>{
                id: mdPath,
                name: mdPath.startsWith('/') ? mdPath.substr(1) : mdPath,
                icon: 'bookmark',
                children: mds,
                expanding: this.expandMenusIdSet.has(mdPath),
            };
            menus.push(mdMenu);
            this.setCateReadmeLink(mdNodes, mdMenu, menuRoutes);
            this.setCateChildrenMarkdownLinks(mdNodes, mds, menuRoutes);
        }
    }

    private setCateReadmeLink(mdNodes: MarkdownMenuNode, menu: Menu, menuRoutes: MenuRoutes) {
        if (mdNodes && mdNodes.readme) {
            mdNodes.used = true;
            menu.icon = 'bookmark_border';
            menu.url = ['doc', this.currentDocId];
            const mdPath = mdNodes.paths + '/README';
            menu.params = {
                r: mdPath
            };
            menu.id = mdPath;
            if (menuRoutes) {
                const routes = menuRoutes.getLeftMenuRoutes(menu, menuRoutes.getComponent());
                if (routes) {
                    routes.forEach(item => menu.url.push(item));
                }
            }
            this.markdownMenuIdMap.set(menu.id, menu);
        }
    }

    private initDoc(doc: Doc) {
        if (doc.markdownMaps) {
            doc.mdRoot = MarkdownMenuNode.getMarkdownDocs(doc.markdownMaps);
        }
        return doc;
    }

    /**
     * 展开/收起菜单
     * @param {Menu} menu 操作的菜单
     * @param {User} user 当前用户
     */
    public expandMenu(menu: Menu, user: User) {
        menu.expanding = !menu.expanding;
        if (!this.expandMenusIdSet) {
            this.expandMenusIdSet = new Set();
        }
        if (menu.expanding) {
            // 展开
            this.expandMenusIdSet.add(menu.id);
        } else {
            // 收起
            this.expandMenusIdSet.delete(menu.id);
        }
        if (menu.children) {
            this.setExpandMenusIdSet(user);
        }
    }

    /**
     * 合并菜单，如果有变更，则返回true
     * @param {ExpandingMenu[]} expandingMenus
     * @returns {boolean} 是否有新的合进来
     */
    public mergeExpandingMenus(expandingMenus: ExpandingMenu[]) {
        const changedMenus: ExpandingMenu[] = [];
        expandingMenus.forEach((item: ExpandingMenu) => {
            let hasChange = false;
            const docId: string = item.docId;
            const menus: string[] = item.expandingMenus.split('|');
            const key = TpStorageService.KEY_MENU + docId;
            const existsMenus = this.tpStorageService.get(key);
            if (existsMenus) {
                // 尝试合并
                const existsMenuArray = existsMenus.split('|');
                existsMenuArray.forEach(m => {
                    if (ArrayUtils.indexOf(menus, m) > -1) {
                        // 已经存在
                        return;
                    }
                    menus.push(m);
                    hasChange = true;
                });

                if (hasChange) {
                    const expandMenusStr = Array.from(menus).join('|');
                    this.tpStorageService.set(key, Array.from(menus).join('|'));

                    changedMenus.push(<ExpandingMenu> {
                        docId: docId,
                        expandingMenus: expandMenusStr
                    });
                    console.log('hasChange:' + key);
                } else {
                    console.log('nochange:' + key);
                }
            } else {
                // 不存在时
                this.tpStorageService.set(key, Array.from(menus).join('|'));
                return;
            }
        });

        if (changedMenus) {
            // 上报
            this.setExpandMenusIds(changedMenus);
        }
    }

    private setSectionMenus(chapter: Chapter, artifact: Artifact, menuRoutes: MenuRoutes, chapterMdNodes: MarkdownMenuNode): Menu[] {
        const sections: Section[] = chapter.sections;
        const menus = [];
        sections.forEach((section: Section) => {
            const sectionName = StringUtils.getFirstLineStr(section.name).trim();
            const chapterName = StringUtils.getFirstLineStr(chapter.name).trim();
            const menuId = chapterName + '/' + sectionName;
            const menu: Menu = <Menu>{
                id: menuId,
                icon: 'class',
                name: sectionName
            };

            const sectionMdNodes = chapterMdNodes ? chapterMdNodes.nodes.get(section.name) : null;
            this.setCateReadmeLink(sectionMdNodes, menu, menuRoutes);

            menus.push(menu);
            if (section.apis) {
                const children = this.getApiMenus(section.apis, artifact, menuRoutes, sectionMdNodes);
                if (children) {
                    children.forEach(child => child.parent = menu);
                    menu.children = children;
                    menu.expanding = this.expandMenusIdSet.has(menu.id);
                }
            }
        });
        this.setCateChildrenMarkdownLinks(chapterMdNodes, menus, menuRoutes);
        return menus;
    }

    private setCateChildrenMarkdownLinks(mdNodes: MarkdownMenuNode, menus: Menu[], menuRoutes: MenuRoutes) {
        if (!mdNodes) {
            return;
        }
        if (mdNodes.nodes && mdNodes.nodes.size > 0) {
            // 创建目录
            mdNodes.nodes.forEach(((value: MarkdownMenuNode, key: string) => {
                if (value.used === true) {
                    return;
                }
                value.used = true;
                const itemPath = value.paths;
                const items = [];
                const menu = <Menu> {
                    id: itemPath,
                    name: key,
                    icon: 'bookmark',
                    children: items
                };
                menus.push(menu);
                this.setCateReadmeLink(value, menu, menuRoutes);
                this.setCateChildrenMarkdownLinks(value, items, menuRoutes);
            }));

        }

        if (mdNodes.items && mdNodes.items.size > 0) {
            const path = mdNodes.paths;
            mdNodes.items.forEach(((value, key) => {
                const mdPath = path + '/' + key;
                menus.push(<Menu> {
                    id: mdPath,
                    name: key,
                    icon: 'bookmark_border',
                    menuType: 100,
                    url: ['doc', this.currentDocId],
                    params: {
                        r: mdPath
                    }
                });
            }));
        }
    }

    private getApiMenus(apis: DocApi[], artifact: Artifact, menuRoutes: MenuRoutes, mdNodes: MarkdownMenuNode): Array<Menu> {
        const menus = [];
        apis.forEach((api: DocApi) => {
            const urls = ['/doc', `${artifact.groupId}:${artifact.artifactId}`, api.key];
            const menu = <Menu>{
                id: api.key,
                name: StringUtils.getFirstLineStr(api.name),
                url: urls,
                menuType: api.type,
                deprecated: !!api.deprecated
            };
            if (menuRoutes) {
                const routes = menuRoutes.getLeftMenuRoutes(menu, menuRoutes.getComponent());
                if (routes) {
                    routes.forEach(item => menu.url.push(item));
                }
            }
            this.markdownMenuIdMap.set(api.key, menu);
            menus.push(menu);
        });
        this.setCateChildrenMarkdownLinks(mdNodes, menus, menuRoutes);
        return menus;
    }

    private getDocHistoryMenu(): Menu {
        return <Menu>{
            id: 'history',
            name: '历史文档',
            icon: 'history',
            url: ['doc', this.currentDocId, 'history'],
            expanding: this.expandMenusIdSet.has('history')
        };
    }

    private addDocManagerMenus(menus: Menu[], user: User) {
        const toolMenus = [];
        toolMenus.push(this.getDocHistoryMenu());

        if (user && ArrayUtils.contain(user.docsOwner, this.currentDocId)) {
            const evnSettingMenu = {
                id: 'envs',
                name: '环境配置',
                icon: 'donut_large',
                expanding: this.expandMenusIdSet.has('envs'),
                url: ['doc', this.currentDocId, 'env']
            };
            const logMenu = {
                id: 'invoke-log',
                name: '调用日志',
                icon: 'date_range',
                expanding: this.expandMenusIdSet.has('invoke-log'),
                url: ['doc', this.currentDocId, 'invoke-log']
            };
            toolMenus.push(evnSettingMenu, logMenu);
        }

        menus.push(<Menu> {
            id: 'tools',
            name: '文档管理',
            icon: 'settings',
            expanding: this.expandMenusIdSet.has('tools'),
            children: toolMenus
        });
    }

    private getExpandMenusIdSet(menuId: string): Set<string> {
        this.currentMenuId = menuId;
        const expandMenusIdSet = new Set<string>();
        const expandMenusIds: string = this.tpStorageService.get(TpStorageService.KEY_MENU + menuId);
        if (expandMenusIds) {
            expandMenusIds.split('|').forEach(item => {
                expandMenusIdSet.add(item);
            });
        }
        return expandMenusIdSet;
    }

    private setExpandMenusIds(expandingMenus: ExpandingMenu[]) {
        // 保存
        this.httpService.post('/api/user/menu-expands/', expandingMenus).subscribe(result => {
            console.log('菜单状态批量保存成功！');
        });
    }

    private setExpandMenusIdSet(user: User) {
        const expandMenus = Array.from(this.expandMenusIdSet).join('|');
        this.tpStorageService.set(TpStorageService.KEY_MENU + this.currentMenuId, expandMenus);

        if (!user) {
            return;
        }
        // 保存
        this.httpService.post('/api/user/menu-expand/', {
            docId: this.currentMenuId,
            expandingMenus: expandMenus
        }).subscribe(result => {
            console.log('菜单状态保存成功！');
        });
    }

    private compareTo(m1: DocMenu, m2: DocMenu): number {
        if (m1.top) {
            if (!m2.top) {
                return -1;
            }
        } else {
            if (m2.top) {
                return 1;
            }
        }
        return m1.index > m2.index ? 1 : -1;
    }

    private isDocOwner(user: User) {
        if (user && user.docsOwner) {
            return ArrayUtils.indexOf(user.docsOwner, this.currentDocId) > -1;
        }
        return false;
    }
}
